package com.huilian.iotbox.data.service.impl;

import com.huilian.iotbox.data.common.ServerResponse;
import com.huilian.iotbox.data.config.AgencyConfig;
import com.huilian.iotbox.data.config.MiddlewareConfig;
import com.huilian.iotbox.data.controller.ControllerException;
import com.huilian.iotbox.data.dao.BaseMemberStoreBalanceDao;
import com.huilian.iotbox.data.dto.*;
import com.huilian.iotbox.data.enums.*;
import com.huilian.iotbox.data.exception.SysException;
import com.huilian.iotbox.data.po.MemberStoreBalance;
import com.huilian.iotbox.data.po.MoneyBalanceRecord;
import com.huilian.iotbox.data.po.Order;
import com.huilian.iotbox.data.service.*;
import com.huilian.iotbox.data.utils.*;
import com.huilian.iotbox.data.vo.MemberStoreBalanceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * @author 谢洛涛
 * @date 2021/7/14 20:14
 */
@Service
@Slf4j
public class RefundServiceImpl implements RefundService {

    @Autowired
    private AgencyConfig agencyConfig;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private BaseOrderService baseOrderService;
    @Autowired
    private BaseMemberStoreBalanceDao baseMemberStoreBalanceDao;
    @Autowired
    private OrderRefundCommonService orderRefundCommonService;
    @Autowired
    private OrderCommonService orderCommonService;
    @Autowired
    private ErrorLogCommonService errorLogCommonService;
    @Autowired
    private CommodityPurchaseRestrictionRecordCommonService commodityPurchaseRestrictionRecordCommonService;

    /**
     * 普通支付订单退款，调用接口退款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderRefund(OrderRefundDto orderRefundDto) throws Exception {
        Order order = baseOrderService.findOneOrderById(orderRefundDto.getOrderId());
        if (StringUtils.isEmpty(order)) {
            throw new ControllerException("订单id参数有误！");
        }

        if (MchTypeEnum.OWN.getCode().compareTo(order.getMchType()) == 0) {
            this.thisPlatformMerchantRefund(order, orderRefundDto);
            if (!StringUtils.isEmpty(order.getMemberId())) {
                this.subtractMemberTotalPay(order.getMemberId(), order.getMerchantStoreId(), order.getTotalAmount(), order.getGmtPayment());
            }
        } else if (MchTypeEnum.PLATFORM.getCode().compareTo(order.getMchType()) == 0) {
            this.thisPlatformMerchantRefund(order, orderRefundDto);
            if (!StringUtils.isEmpty(order.getMemberId())) {
                this.subtractMemberTotalPay(order.getMemberId(), order.getMerchantStoreId(), order.getTotalAmount(), order.getGmtPayment());
            }
        } else if (MchTypeEnum.CASH_RECEIPTS.getCode().compareTo(order.getMchType()) == 0) {
            this.cashCollection(order, orderRefundDto);
        } else if (MchTypeEnum.OFFLINE_COLLECTION_CODE.getCode().compareTo(order.getMchType()) == 0) {
            this.thisPlatformMerchantRefund(order, orderRefundDto);
            if (!StringUtils.isEmpty(order.getMemberId())) {
                this.subtractMemberTotalPay(order.getMemberId(), order.getMerchantStoreId(), order.getTotalAmount(), order.getGmtPayment());
            }
        } else {
            throw new RuntimeException("当前订单类型暂不支持退款，订单类型：" + order.getMchType() + "。");
        }
    }


    /**
     * 本平台商户退款
     *
     * @param order
     * @param orderRefundDto
     */
    private void thisPlatformMerchantRefund(Order order, OrderRefundDto orderRefundDto) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
        orderRefundDto.setOutRefundNo("refund" + Util.getOutTradeNo());
        Map<String, Object> form = new HashMap<>();
        form.put("HlMerchantId", order.getMerchantId());
        form.put("OutTradeNo", order.getOutTradeNo());
        form.put("OutRefundNo", orderRefundDto.getOutRefundNo());
        form.put("RefundAmount", order.getTotalAmount().multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_HALF_UP));
        form.put("RefundReason", orderRefundDto.getRefundReason());
        String param = HttpUtils.format(agencyConfig, form, FunctionEnum.ORDER_REFUND);
        String response = HttpsMain.httpsReq(MiddlewareConfig.orderUrl, param);
//        log.info("请求响应结果{}", response);
        //延签并返回map值
        final Map<String, Object> result = HttpUtils.getResultData(response);
        final String status = (String) result.get("status");
        final String code = (String) result.get("code");
        if (status.equals(RequestStatus.SUCCESS.getCode()) && code.equals(RequestStatus.SUCCESS_CODE.getCode())) {
            log.info("申请退款成功后发送MQ：{}", System.currentTimeMillis());
            orderRefundDto.setMerchantId(order.getMerchantId());
            orderRefundDto.setRefundAmount(order.getTotalAmount());
            orderRefundDto.setOutTradeNo(order.getOutTradeNo());
            orderRefundDto.setPayOrderId(order.getId()); // 插入原订单号
            CorrelationData correlationData = new CorrelationData(orderRefundDto.getOutRefundNo());
            rabbitTemplate.convertAndSend("iotBox-order-Refund", "iotBox-order-Refund.iotBoxOrderRefund", orderRefundDto, correlationData);
        } else {
            throw new ControllerException((String) result.get("msg"));
        }
    }

    /**
     * 现金收款
     *
     * @param order
     * @param orderRefundDto
     */
    @Transactional(rollbackFor = Exception.class)
    public void cashCollection(Order order, OrderRefundDto orderRefundDto) {
        // 修改订单状态
        this.updateOrderRefundStatus(order, orderRefundDto);
        // 修改会员限购记录状态
        ServerResponse serverResponse = commodityPurchaseRestrictionRecordCommonService.updateCommodityPurchaseRestrictionRecordStatusPack(order.getId(), 2);
        if (!serverResponse.getIsSuccess()) {
            errorLogCommonService.writerInfoErrorLog("修改会员限购记录状态失败", order);
            throw new SysException(serverResponse.getMsg());
        }
    }
    /**
     * 退款后在会员总消费金额减去退款金额
     *
     * @param memberId
     * @param merchantStoreId
     * @param totalAmount
     * @param gmtPayment
     */
    private void subtractMemberTotalPay(Long memberId, Integer merchantStoreId, BigDecimal totalAmount, Date gmtPayment) {
        MemberStoreBalanceDto memberStoreBalanceDto = new MemberStoreBalanceDto();
        memberStoreBalanceDto.setMemberId(memberId);
        memberStoreBalanceDto.setMerchantStoreId(merchantStoreId);
        final MemberStoreBalanceVo balanceByMerchantStoreId = baseMemberStoreBalanceDao.findMemberStoreBalanceByMerchantStoreId(memberStoreBalanceDto);
        balanceByMerchantStoreId.setTotalPay(balanceByMerchantStoreId.getTotalPay().subtract(totalAmount));
        balanceByMerchantStoreId.setLatelyPay(gmtPayment);
        if (balanceByMerchantStoreId.getTotalPay().compareTo(new BigDecimal("20000")) > 0) {
            balanceByMerchantStoreId.setLevelPay(3);
        } else if (balanceByMerchantStoreId.getTotalPay().compareTo(new BigDecimal("5000")) > 0) {
            balanceByMerchantStoreId.setLevelPay(2);
        }
        baseMemberStoreBalanceDao.updateOrderRecord(balanceByMerchantStoreId);
    }

    private void updateOrderRefundStatus(Order order, OrderRefundDto orderRefundDto) {
        if (ObjectUtils.isEmpty(order.getOutTradeNo())) {
            throw new SysException("订单号不存在");
        }
        // 查询order_refund表是否有退款订单了
        Integer count = orderRefundCommonService.findOrderRefundCountByOutTradeNo(order.getOutTradeNo());
        if (count > 0) {
            throw new SysException("请不要重复退款");
        }
        OrderRefundDto item = new OrderRefundDto();
        try {
            item = orderRefundCommonService.getRefundOrderByOutradeNo(order.getOutTradeNo());
        } catch (Exception e) {
            errorLogCommonService.writerInfoException("退款异常", order, e);
            e.printStackTrace();
            throw new SysException("退款异常");
        }
        try {
            orderRefundDto.setOutRefundNo("refund" + Util.getOutTradeNo());
            orderRefundDto.setMerchantId(order.getMerchantId());
            orderRefundDto.setRefundAmount(order.getTotalAmount());
            orderRefundDto.setOutTradeNo(order.getOutTradeNo());
            orderRefundDto.setRefundStatus(RefundStatusEnum.REFUNDING.getCode());
            // 插入退款记录
            orderRefundDto.setPayOrderId(order.getId()); // 插入原订单号
            orderRefundDto.setOrderId(item.getOrderId());
            orderRefundDto.setBalance(SciCalUtils.sciCalToBD(item.getBalance().subtract(orderRefundDto.getRefundAmount()), 2));
            orderRefundDto.setPayCompanyCode(orderRefundDto.getPayCompanyCode());
            orderRefundDto.setDeviceNo(item.getDeviceNo());
            orderRefundCommonService.insert(orderRefundDto);
        } catch (Exception e) {
            errorLogCommonService.writerInfoException("退款订单入库出错", orderRefundDto, e);
            throw new SysException("退款订单处理出错");
        }
        try {
            //判断是否是隔天退款，如果是隔天退款的话，订单已经结算打款，所以订单我们不再做处理，copy一条订单到今天 , 分润再订
            Integer difDay = baseOrderService.checkOrderAndRefundDateDif(order.getId());
            if (difDay >= 1) {
                Order difOrder = new Order();
                BeanUtils.copyProperties(order, difOrder);
                log.info("隔天退单");
                difOrder.setOutTradeNo("隔天退单-" + difOrder.getOutTradeNo() + "-" + (new Random().nextInt(90) + 10));
                //退款金额
                BigDecimal refundAmount = orderRefundDto.getRefundAmount();
                //订单状态修改为 全额退款
                difOrder.setStatus(OrderStatusEnum.FULL_FERUND.getCode());
                //交易总金额
                difOrder.setTotalAmount(SciCalUtils.sciCalToBD(BigDecimal.ZERO.subtract(refundAmount), 2));
                //申请退款金额
                difOrder.setRefundTotalAmount(SciCalUtils.sciCalToBD(refundAmount, 2));
                difOrder.setRefundType(RefunTypeEnum.THE_NEXT_DAY_A_REFUND.getCode());
                //结算金额
                difOrder.setSettlementMoney(SciCalUtils.sciCalToBD(BigDecimal.ZERO.subtract(orderRefundDto.getRefundAmount()), 2));
                difOrder.setSaleCoin(0 - difOrder.getSaleCoin());
                difOrder.setGiveCoin(0 - difOrder.getGiveCoin());
                difOrder.setTotalCoin(0 - difOrder.getTotalCoin());
                difOrder.setRechargeAfterBalance(0);
                difOrder.setRechargeBeforeBalance(0);
                //手续费
                difOrder.setHandlingCharge(BigDecimal.ZERO);
                difOrder.setStmtStatus(0);
                //清除支付完成时间
                difOrder.setGmtPayment(new Date());
                orderCommonService.addOrder(difOrder);
                orderRefundDto.setRefundStatus(RefundStatusEnum.SUCC.getCode());
                orderRefundDto.setOrderId(difOrder.getId());  //新隔天退款订单orderId，用于退款查询刷新新订单状态
                orderRefundDto.setGmtRefundment(difOrder.getGmtPayment());
                orderRefundCommonService.updateOrderRefund(orderRefundDto);
            } else {
                log.info("当天退单");
                OrderDto updateOrder = new OrderDto();
                updateOrder.setId(Long.valueOf(orderRefundDto.getOrderId()));
                // 修改为全额退款
                updateOrder.setStatus(OrderStatusEnum.FULL_FERUND.getCode());
                updateOrder.setRefundType(RefunTypeEnum.ON_THE_DAY_OF_THE_REFUND.getCode());
                orderCommonService.updateStatusById(updateOrder);
            }

        } catch (Exception e) {
            errorLogCommonService.writerInfoException("退款订单处理出错", orderRefundDto, e);
            e.printStackTrace();
            throw new SysException("退款订单处理出错");
        }
    }
}
